#include "qe_log.h"
#include "qe_bitops.h"
#include "qe_assert.h"
#include "qe_memory.h"
#include "qe_driver.h"
#include "qe_serial.h"
#include "qe_earlycon.h"
#include "qe_interrupt.h"
#include "libfdt.h"



QELOG_DOMAIN("axi-uart16550")



#define UART16550_LSR_DR                0x00000001  /* Data Ready */
#define UART16550_LSR_THRE              0x00000020  /* Transmitter Holding Register Empty */
#define UART16550_LSR_BI                QE_BIT(4)   /* Break Interrupt */
#define UART16550_LCR_STOPMODE_2_BIT    0x00000004  /* 2 stop bits */
#define UART16550_LCR_STOPMODE_1_5_BIT  0x00000004  /* 1.5 stop bits */
#define UART16550_LCR_STOPMODE_1_BIT    0x00000000  /* 1 stop bit */
#define UART16550_LCR_STOPMODE_MASK     0x00000004  /* Stop bits mask */
#define UART16550_LCR_PARITY_NONE       0x00000000  /* No parity mode */
#define UART16550_LCR_PARITY_EVEN       0x00000018  /* Even parity mode */
#define UART16550_LCR_PARITY_ODD        0x00000008  /* Odd parity mode */
#define UART16550_LCR_PARITY_MASK       0x00000038  /* Parity mask */
#define UART16550_LCR_8_BIT             0x00000003  /* 8bits/character */
#define UART16550_LCR_7_BIT             0x00000002  /* 7bits/character */
#define UART16550_LCR_6_BIT             0x00000001  /* 6bits/character */
#define UART16550_LCR_5_BIT             0x00000000  /* 5bits/character */
#define UART16550_LCR_BIT_MASK          0x00000003  /* Bit mask */
#define UART16550_IER_RX_EN             QE_BIT(0)   /* Enable Received Data Available Interrupt */
#define UART16550_IER_TX_EN             QE_BIT(1)   /* Enable Transmitter Holding Register Empty Interrupt */
#define UART16550_FCR_FIFO_TRI_MASK     0x000000C0  /* RCVR FIFO Trigger mask */
#define UART16550_FCR_FIFO_TRI_14       0x000000C0  /* RCVR FIFO Trigger 14 bytes */
#define UART16550_FCR_FIFO_TRI_8        0x00000080  /* RCVR FIFO Trigger 8 bytes */
#define UART16550_FCR_FIFO_TRI_4        0x00000040  /* RCVR FIFO Trigger 4 bytes */
#define UART16550_FCR_FIFO_TRI_1        0x00000000  /* RCVR FIFO Trigger 1 bytes */
#define UART16550_FCR_FIFO_EN           0x00000001  /* FIFO Enable */
#define UART16550_FCR_FIFO_RESET        0x00000006  /* FIFO Reset */

#define UART16550_IIR_TX_EMPTY          QE_BIT(1)   /* Transmitter Holding Register Empty */
#define UART16550_IIR_RX_READY          0x00000004  /* Received Data Available */
#define UART16550_IIR_RX_TMO            0x0000000C  /* Character Timeout */
#define UART16550_IIR_RX_OVER           0x00000001

#define UART16550_MAX_RATE              8000000U
#define UART16550_MIN_RATE              110U
#define UART16550_MAX_BAUD_ERROR_RATE   3U	/* max % error allowed */


typedef struct {
    union {
        volatile qe_u32 rbr;   /* RO, Receiver Buffer Register */
        volatile qe_u32 thr;   /* WO, Transmitter Holding Register */
        volatile qe_u32 dll;   /* RW, Divisor Latch (Least Significant Byte) Register */
    };

    union {
        volatile qe_u32 ier;   /* Interrupt Enable Register */
        volatile qe_u32 dlm;   /* Divisor Latch (Most Significant Byte) Register */
    };

    union {
        volatile qe_u32 iir;   /* Interrupt Identification Register*/
        volatile qe_u32 fcr;   /* FIFO Control Register  */
    };

    volatile qe_u32 lcr;       /* Line Control Register */
    volatile qe_u32 mcr;       /* Modem Control Register */
    volatile qe_u32 lsr;       /* Line Status Register */
    volatile qe_u32 msr;       /* Modem Status Register */
    volatile qe_u32 scr;       /* Scratch Register */
} uart16550_regs;

typedef struct
{
    volatile qe_u32 en;
} uart16550_freeze_reg;

typedef struct {
    uart16550_regs *regs;
    uart16550_freeze_reg *freeze;
    qe_serial_dev serial;
    qe_intc_dev *intc;
    qe_uint irq;
    qe_uint clk;
    qe_uint baudrate;
    qe_uint buffer_size;
    qe_uint rx_trigger_level;
    qe_uint rx_indicate_level;
    qe_bool is_transfer_started;
    qe_bool is_axi_intc;
} uart16550_dev;

static void uart16550_irq_enable(uart16550_dev *dev, qe_u16 flag)
{
    qe_u32 mask = 0x0;

    if (flag & QE_DEV_F_INT_RX)
        mask |= UART16550_IER_RX_EN;
    if (flag & QE_DEV_F_INT_TX)
        mask |= UART16550_IER_TX_EN;
    dev->regs->ier |= mask;
}

static void uart16550_irq_disable(uart16550_dev *dev)
{
    dev->regs->ier &= ~(UART16550_IER_RX_EN | UART16550_IER_TX_EN);
}

static void qe_unused uart16550_dlab_switch(uart16550_dev *dev, qe_bool bit)
{
    if (bit)
        dev->regs->lcr |= 0x00000080;
    else
        dev->regs->lcr &= ~0x00000080;
}

static void uart16550_fifo_reset(uart16550_dev *dev)
{
    dev->regs->fcr |= UART16550_FCR_FIFO_RESET;
    dev->regs->fcr &= ~UART16550_FCR_FIFO_RESET;
    dev->regs->fcr = UART16550_FCR_FIFO_EN | UART16550_FCR_FIFO_TRI_8;
}

static qe_ret uart16550_set_rx_trigger(uart16550_dev *dev, qe_u8 level)
{
    qe_u32 trigger_level = 0x0;

    switch (level) {

    case 1:
        trigger_level = UART16550_FCR_FIFO_TRI_1;
        break;

    case 4:
        trigger_level = UART16550_FCR_FIFO_TRI_4;
        break;

    case 8:
        trigger_level = UART16550_FCR_FIFO_TRI_8;
        break;

    case 14:
        trigger_level = UART16550_FCR_FIFO_TRI_14;
        break;

    default:
        qe_error("unsupport level:%d", level);
        return qe_err_notsupport;
    }

    dev->regs->fcr = UART16550_FCR_FIFO_EN | trigger_level;

    qe_info("rx trigger level:%d", level);

    return qe_ok;
}

static qe_ret uart16550_set_baudrate(uart16550_regs *reg, qe_u32 clk, qe_u32 baudrate)
{
    qe_u16 divisor;
    qe_u32 lsb;
    qe_u32 msb;

    if ((baudrate > (qe_u32)UART16550_MAX_RATE) ||
        (baudrate < (qe_u32)UART16550_MIN_RATE)) {
        qe_error("baud %d range err", baudrate);
        return qe_err_range;
    }

    reg->lcr |= 0x00000080;

    divisor = (clk + (baudrate * 16UL)/2) / (baudrate * 16UL);
    lsb = divisor & 0x000000FF;
    msb = (divisor >> 8) & 0x000000FF;
    reg->dll = 0xFF;
    reg->dlm = msb;
    reg->dll = lsb;

    reg->lcr &= ~0x00000080;

    qe_info("set baudrate %d", baudrate);

    return qe_ok;
}

static void uart16550_xfer(qe_serial_dev *serial, uart16550_dev *dev)
{
    int i;
    char fifo[16];
    qe_uint wait;
    qe_uint read_once;
    wait = qe_ringbuffer_wait(serial->tx_ring);
    read_once = qe_min(wait, 16);
    if (!read_once)
        return;
    qe_ringbuffer_read(serial->tx_ring, fifo, read_once);
    for (i=0; i<read_once; i++) {
    	dev->regs->thr = fifo[i];
    }
}

static qe_int uart16550_transfer_start(qe_serial_dev *serial)
{
    uart16550_dev *dev;

    qe_assert(serial != QE_NULL);

    dev = (uart16550_dev *)serial->parent.priv;
    qe_assert(dev != QE_NULL);

    if (dev->is_transfer_started) {
        qe_warning("transfer already started");
        return -(qe_err_common);
    }
    dev->is_transfer_started = qe_true;

    /* do one xfer */
    uart16550_xfer(serial, dev);

    return 0;
}

static void uart16550_tx_isr(qe_serial_dev *serial, uart16550_dev *dev)
{
    int wait;

    if (!dev->is_transfer_started)
        return;

    wait = qe_ringbuffer_wait(serial->tx_ring);
    if (!wait) {
        dev->is_transfer_started = qe_false;
        if (serial->parent.tx_complete)
        	serial->parent.tx_complete(&serial->parent, QE_NULL);
    } else {
        uart16550_xfer(serial, dev);
    }
}

static void uart16550_rx_isr(qe_serial_dev *serial, uart16550_dev *dev, qe_u32 iir)
{
    char data;
    qe_uint wait;
    uart16550_regs *regs = dev->regs;

    if (!serial->rx_ring)
        return;

    while (regs->lsr & UART16550_LSR_DR) {
        data = regs->rbr;
        qe_ringbuffer_write(serial->rx_ring, &data, 1);
    }

    if (iir & (UART16550_IIR_RX_READY | UART16550_IIR_RX_TMO)) {
        // if (regs->lsr & UART16550_LSR_BI)
        //     data = regs->rbr;
        // while (read_bytes < (dev->rx_trigger_level)) {
        //     lsr = regs->lsr;
        //     if (lsr & UART16550_LSR_BI) {
        //         data = regs->rbr;
        //     } else if (lsr & UART16550_LSR_DR) {
        //         data = regs->rbr;
        //         qe_ringbuffer_write(serial->rx_ring, &data, 1);
        //         read_bytes++;
        //     } else {
        //         break;
        //     }
        // }
    }

    wait = qe_ringbuffer_wait(serial->rx_ring);
    //qe_debug("wait:%d", wait);
    if ((iir & UART16550_IIR_RX_OVER) || 
        (iir & UART16550_IIR_RX_TMO) ||
        (wait >= dev->rx_indicate_level)) {
        if (serial->parent.rx_indicate) {
            serial->parent.rx_indicate(&serial->parent, wait);
        }
    }
}

static void uart16550_isr(void *arg)
{
    qe_u32 iir;

    qe_serial_dev *serial = (qe_serial_dev *)arg;
    uart16550_dev *dev = (uart16550_dev *)serial->parent.priv;

    iir = dev->regs->iir;
    //qe_debug("isr %x", iir);

    if (iir & (UART16550_IIR_RX_READY |
               UART16550_IIR_RX_TMO |
               UART16550_IIR_RX_OVER)) {
        uart16550_rx_isr(serial, dev, iir);
    }

    if (iir & UART16550_IIR_TX_EMPTY) {
        uart16550_tx_isr(serial, dev);
    }
}

static qe_ret uart16550_configure(qe_serial_dev *serial,
    qe_serial_configure *cfg)
{
    qe_ret ret;
    qe_u32 lcr;

    uart16550_dev *dev = serial->parent.priv;
    uart16550_regs *regs = dev->regs;

    uart16550_irq_disable(dev);
    uart16550_fifo_reset(dev);

    lcr = regs->lcr & ~(UART16550_LCR_STOPMODE_MASK |
                        UART16550_LCR_PARITY_MASK |
                        UART16550_LCR_BIT_MASK);

    /* stop bits */
    if (cfg->stop_bits == STOP_BITS_2) {
        lcr |= UART16550_LCR_STOPMODE_2_BIT;
    } else if (cfg->stop_bits == STOP_BITS_1) {
        lcr |= UART16550_LCR_STOPMODE_1_BIT;
    } else {
    	qe_error("stb not support");
        return qe_err_notsupport;
    }

    /* parity */
    if (cfg->parity == PARITY_EVEN) {
        lcr |= UART16550_LCR_PARITY_EVEN;
    } else if (cfg->parity == PARITY_ODD) {
        lcr |= UART16550_LCR_PARITY_ODD;
    } else if (cfg->parity == PARITY_NONE) {
        lcr |= UART16550_LCR_PARITY_NONE;
    } else {
        qe_error("err parity:%d", cfg->parity);
        return qe_err_param;
    }

    /* data bits */
    if (cfg->data_bits == DATA_BITS_8) {
        lcr |= UART16550_LCR_8_BIT;
    } else if (cfg->data_bits == DATA_BITS_7) {
        lcr |= UART16550_LCR_7_BIT;
    } else if (cfg->data_bits == DATA_BITS_6) {
        lcr |= UART16550_LCR_6_BIT;
    } else if (cfg->data_bits == DATA_BITS_5) {
        lcr |= UART16550_LCR_5_BIT;
    } else {
        qe_error("err dbits:%d", cfg->data_bits);
        return qe_err_param;
    }

    regs->lcr = lcr;

    regs->fcr = UART16550_FCR_FIFO_EN;

    ret = uart16550_set_rx_trigger(dev, dev->rx_trigger_level);
    if (ret != qe_ok) {
        return ret;
    }

    ret = uart16550_set_baudrate(dev->regs, dev->clk, cfg->baud_rate);
    if (ret != qe_ok) {
        qe_error("baudrate set err:%d", ret);
        return ret;
    }

    return qe_ok;
}

static int uart16550_putc(qe_serial_dev *serial, char c)
{
    uart16550_dev *dev = serial->parent.priv;

    qe_assert(serial != QE_NULL);
	qe_assert(dev != QE_NULL);

    do {
		while (!(dev->regs->lsr & UART16550_LSR_THRE));
		dev->regs->thr = c;
	} while (0);

    return 1;
}

static int uart16550_getc(qe_serial_dev *serial)
{
	int c;
	uart16550_dev *dev = (uart16550_dev *)serial->parent.priv;

	qe_assert(serial != QE_NULL);
	qe_assert(dev != QE_NULL);

	c = -1;

    while (!(dev->regs->lsr & UART16550_LSR_DR))
        ;
	
	c = dev->regs->rbr;

	return c;
}

static qe_ret uart16550_ioctl(qe_serial_dev *serial, int cmd, void *arg)
{
    qe_u16 int_flag;
    //qe_uint rx_level;
    uart16550_dev *uart = (uart16550_dev *)serial->parent.priv;

    switch (cmd) {

    case QE_DEV_IOC_CLR_INT:
        if (!uart->intc) {
            qe_error("%s no intc", serial->parent.name);
            return qe_err_notsupport;
        }
        uart16550_irq_disable(uart);
        qe_intc_irq_disable(uart->intc, uart->irq);
        qe_info("%s disable irq:%d", serial->parent.name, uart->irq);
        break;

    case QE_DEV_IOC_SET_INT:
        if (!uart->intc) {
            qe_error("%s no intc", serial->parent.name);
            return qe_err_notsupport;
        }
        int_flag = *(qe_u16 *)arg;
        qe_intc_irq_enable(uart->intc, uart->irq);
        uart16550_irq_enable(uart, int_flag);
        qe_info("%s enable irq:%d", serial->parent.name, uart->irq);
        break;

//    case QE_SERIAL_IOC_RX_LVL:
//        rx_level = *(qe_uint *)arg;
//        uart->rx_indicate_level = rx_level;
//        break;
    }

    return qe_ok;
}

static qe_uart_ops uart16550_ops = {
	.configure      = uart16550_configure,
	.ioctl          = uart16550_ioctl,
    .getc           = uart16550_getc,
    .putc           = uart16550_putc,
    .transfer_start = uart16550_transfer_start,
};

static void serial_dev_register(uart16550_dev *dev, const char *name)
{
    qe_serial_configure config = QE_SERIAL_DEFCONFIG;

    config.baud_rate = dev->baudrate;
    config.bufsz = dev->buffer_size;
    qe_serial_dev *serial = &dev->serial;

    serial->ops = &uart16550_ops;
    dev->is_transfer_started = qe_false;

    serial->config = config;
    qe_serial_register(serial, name,
        QE_DEV_F_INT_RX|QE_DEV_F_INT_TX, dev);

    qe_debug("register %s %p", name, dev->regs);
}

static qe_ret uart16550_probe(const void *fdt, int offset)
{
    int len;
    qe_ret ret;
    const char *name;
    const char *name_intc;
    const qe_u32 *prop_reg;
    const qe_u32 *prop_clk;
    const qe_u32 *prop_irq;
    const qe_u32 *prop_baud;
    const qe_u32 *prop_bufsz;
    const qe_u32 *prop_trigger;
    qe_intc_dev *intc;
    uart16550_dev *dev;

    name = fdt_get_name(fdt, offset, QE_NULL);

    prop_reg = (const qe_u32 *)fdt_getprop(fdt, offset, "reg", &len);
    if (!prop_reg) {
        qe_error("%s no reg prop", name);
        return qe_err_param;
    }

    prop_clk = (const qe_u32 *)fdt_getprop(fdt, offset, "clk", &len);
    if (!prop_clk) {
        qe_error("%s no clk prop", name);
        return qe_err_param;
    }

    prop_baud = (const qe_u32 *)fdt_getprop(fdt, offset, "baudrate", &len);
    if (!prop_baud) {
        qe_error("%s no baudrate prop", name);
        return qe_err_param;
    }

    name_intc = qe_of_get_interrupt_parent_name(fdt, offset);
    if (name_intc) {
        qe_debug("%s has interrupt parent %s", name_intc);
        intc = (qe_intc_dev *)qe_dev_find(name_intc);
        if (!intc) {
            qe_error("intc %s not found", name_intc);
            return qe_err_notfind;
        }
        prop_irq = (const qe_u32 *)fdt_getprop(fdt, offset, "interrupts", &len);
        if (!prop_irq) {
            qe_error("%s no interrupts prop", name);
            return qe_err_param;
        }
    }

    prop_trigger = (const qe_u32 *)fdt_getprop(fdt, offset, "trigger", &len);
    if (!prop_trigger) {
        qe_error("%s no trigger prop", name);
        return qe_err_param;
    }

    prop_bufsz = (const qe_u32 *)fdt_getprop(fdt, offset, "bufsz", &len);

    dev = qe_malloc(sizeof(uart16550_dev));
    qe_assert(dev);

    dev->regs = (uart16550_regs *)(qe_ubase)fdt32_to_cpu(*prop_reg);
    dev->clk = fdt32_to_cpu(*prop_clk);
    dev->baudrate = fdt32_to_cpu(*prop_baud);
    dev->rx_trigger_level = fdt32_to_cpu(prop_trigger[0]);
    dev->rx_indicate_level = fdt32_to_cpu(prop_trigger[1]);

    if (prop_bufsz) {
        dev->buffer_size = fdt32_to_cpu(*prop_bufsz);
    } else {
        dev->buffer_size = 64;
    }

    qe_debug("regs %x", dev->regs);
    qe_debug("clk %d", dev->clk);
    qe_debug("baudrate %d", dev->baudrate);

    if (name_intc) {
        dev->intc = intc;
        dev->irq = fdt32_to_cpu(prop_irq[1]);
        ret = qe_intc_irq_register(dev->intc, dev->irq, uart16550_isr, &dev->serial);
        if (ret != qe_ok) {
            qe_error("%s register irq %d err:%d", name, dev->irq, ret);
            qe_free(dev);
            return ret;
        }
        qe_debug("register irq:%d", dev->irq);
    }

    serial_dev_register(dev, name);

    return qe_ok;
}

static const qe_device_id uart16550_ids[] = {
    {.compatible = "xlnx,axi-uart16550",},
    {}
};

QE_DRIVER(axi_uart16550) = {
    .name = "uart16550",
    .of_match = uart16550_ids,
    .probe = uart16550_probe,
};

QE_DRIVER_FORCE_EXPORT(axi_uart16550);


#if defined(CONFIG_AXI_UART16550_EARLYCON)
static void uart16550_earlycon_init(const void *fdt, int offset)
{
	int len;
    qe_u32 clk;
    qe_u32 baudrate;
    const qe_u32 *prop_reg;
    const qe_u32 *prop_clk;
    const qe_u32 *prop_baud;

    prop_reg = (const qe_u32 *)fdt_getprop(fdt, offset, "reg", &len);
    if (!prop_reg) {
        return;
    }

    prop_clk = (const qe_u32 *)fdt_getprop(fdt, offset, "clk", &len);
    if (!prop_clk) {
        return;
    }

    prop_baud = (const qe_u32 *)fdt_getprop(fdt, offset, "baudrate", &len);
    if (!prop_baud) {
        return;
    }

    uart16550_regs *reg = (uart16550_regs *)(qe_ubase)fdt32_to_cpu(*prop_reg);
    clk = fdt32_to_cpu(*prop_clk);
    baudrate = fdt32_to_cpu(*prop_baud);
    uart16550_set_baudrate(reg, clk, baudrate);
}

void uart16550_earlycon_putc(qe_ubase base, char c)
{
    uart16550_regs *reg = (uart16550_regs *)base;
    
    while (!(reg->lsr & UART16550_LSR_THRE))
        ;

    reg->thr = c;
}

QE_EARLYCON(axi_uart16550) = {
	.compatible = "xlnx,axi-uart16550",
	.init = uart16550_earlycon_init,
	.putc = uart16550_earlycon_putc
};

QE_EARLYCON_FORCE_EXPORT(axi_uart16550)
#endif
